home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / smail-3.1.28 / src / string.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-20  |  17.1 KB  |  785 lines

  1. /* @(#)src/string.c    1.14 9/20/92 12:55:06 */
  2.  
  3. /*
  4.  *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  5.  *    Copyright (C) 1992  Ronald S. Karr
  6.  * 
  7.  * See the file COPYING, distributed with smail, for restriction
  8.  * and warranty information.
  9.  */
  10.  
  11. /*
  12.  * strings.c:
  13.  *    miscellaneous string operations
  14.  *
  15.  *    external functions:  strcmpic, strncmpic, strip, strcolon,
  16.  *                 is_string_in_list, strerrno, strsysexit,
  17.  *                 str_printf, xprintf, dprintf, c_atol,
  18.  *                 base62, read_line, str_cat, str_ncat,
  19.  *                 vfprintf, ivaltol, ltoival, copy, rcopy
  20.  */
  21. #include <stdio.h>
  22. #include <ctype.h>
  23. #include "defs.h"
  24. #ifndef STANDALONE
  25. # include <varargs.h>
  26. # include "smail.h"
  27. # include "dys.h"
  28. # ifndef DEPEND
  29. #  include "extern.h"
  30. #  include "exitcodes.h"
  31. # endif
  32. #else /* STANDALONE */
  33. extern char lowcase[];            /* lower case conversion table */
  34. extern char upcase[];            /* upper case conversion table */
  35. #endif /* STANDALONE */
  36.  
  37. /* functions local to this file */
  38. static char *bltoa();
  39. static void str_printf_va();
  40.  
  41.  
  42. /*
  43.  * strcmpic - case independent strcmp function
  44.  */
  45. int
  46. strcmpic(s1, s2)
  47.     register char *s1, *s2;        /* strings to be compared */
  48. {
  49.     register int c1, c2;        /* temp */
  50.  
  51.     while (*s1 && *s2) {
  52.     if ((c1 = lowercase(*s1++)) != (c2 = lowercase(*s2++))) {
  53.         return c1-c2;
  54.     }
  55.     }
  56.  
  57.     /*
  58.      * one or both chars must be `\0'.  If only one is `\0', then
  59.      * the other string is longer.
  60.      */
  61.     return (int)((*s1)-(*s2));
  62. }
  63.  
  64. /*
  65.  * strcmpic - case independent strcmp function
  66.  */
  67. int
  68. strncmpic(s1, s2, n)
  69.     register char *s1, *s2;        /* strings to compare */
  70.     int n;                /* compare up to this many chars */
  71. {
  72.     register int c1, c2;        /* temp */
  73.     register int cnt = n;        /* count of chars so far compared */
  74.  
  75.     while (*s1 && *s2 && cnt > 0) {
  76.     if ((c1 = lowercase(*s1++)) != (c2 = lowercase(*s2++))) {
  77.         return c1-c2;
  78.     }
  79.     cnt--;                /* count this character */
  80.     }
  81.  
  82.     /*
  83.      * If we ran out of chars, then the string segments are equal, otherwise
  84.      * one or both strings must have ended. In this case the subtraction
  85.      * will show which one is shorter, if any.
  86.      */
  87.     return cnt ? (int)((*s1)-(*s2)) : 0;
  88. }
  89.  
  90.  
  91. /*
  92.  * strip - strip quotes and escaped characters
  93.  */
  94. int
  95. strip(addr)
  96.     register char *addr;        /* strip this address */
  97. {
  98.     int was_stripped = FALSE;        /* TRUE if any stripping was done */
  99.     register char *p = addr;        /* write pointer to addr */
  100.     register int c;            /* read char in addr */
  101.  
  102.     while (c = *addr++) {
  103.     if (c == '\\') {        /* skip to char after \ */
  104.         *p++ = *addr++;
  105.         was_stripped = TRUE;
  106.     } else if (c == '"') {        /* don't copy quote char */
  107.         was_stripped = TRUE;
  108.     } else {
  109.         *p++ = c;
  110.     }
  111.     }
  112.     *p++ = '\0';            /* end of string */
  113.  
  114.     return was_stripped;
  115. }
  116.  
  117. /*
  118.  * strcolon - step through string parts separated by colons
  119.  *
  120.  * when called with a string, return a copy of the first part of
  121.  * the string up to, but excluding the first `:'.  When called with
  122.  * NULL return a copy of the next part of the previously passed string,
  123.  * with each part separated by a colon `:'.
  124.  *
  125.  * return NULL if no more parts are left.
  126.  *
  127.  * strcolon is typically used in a loop on ':' separated names such as:
  128.  *
  129.  *    for (p = strcolon(names); p; p = strcolon((char *)NULL)) {
  130.  *        ... do something with the name p ...
  131.  *    }
  132.  *
  133.  * the malloc region returned is reused, so if you wish to keep a string
  134.  * around, you will need to copy it.
  135.  */
  136. char *
  137. strcolon(s)
  138.     register char *s;            /* string or NULL */
  139. {
  140.     static char *next = NULL;        /* pointer to next ':' */
  141.     static char *region = NULL;        /* region used to store result */
  142.     static int alloc = 0;        /* alloc size of region */
  143.  
  144.     if (!s) {
  145.     s = next;
  146.     if (s == NULL) {
  147.         return NULL;
  148.     }
  149.     }
  150.     next = index(s, ':');
  151.     if (next) {
  152.     register int len = next - s;
  153.  
  154.     if (len >= alloc) {
  155.         if (region == NULL) {
  156.         region = xmalloc(alloc = len + 1);
  157.         } else {
  158.         region = xrealloc(region, alloc = len + 1);
  159.         }
  160.     }
  161.     (void) memcpy(region, s, next - s);
  162.     region[next - s] = '\0';
  163.     next++;
  164.     return region;
  165.     }
  166.     return s;
  167. }
  168.  
  169. /*
  170.  * is_string_in_list - return true if string is in colon-separated list
  171.  *
  172.  * given a string and a colon separated list of strings, return TRUE
  173.  * if the given string is in the list, else FALSE.  Case is not
  174.  * significant in comparisons.
  175.  */
  176. int
  177. is_string_in_list(string, list)
  178.     register char *string;        /* string to look for */
  179.     char *list;                /* list of strings */
  180. {
  181.     register char *s;
  182.  
  183.     for (s = strcolon(list); s; s = strcolon((char *)NULL)) {
  184.     if (EQIC(string, s)) {
  185.         return TRUE;
  186.     }
  187.     }
  188.  
  189.     return FALSE;
  190. }
  191.  
  192.  
  193. /*
  194.  * strerrno -  return a string representing the error stored in errno.
  195.  */
  196. char *
  197. strerrno()
  198. {
  199.     static char misc_err[50];        /* used when sprintf must be used */
  200.     extern char *sys_errlist[];        /* list of error strings */
  201.     extern int sys_nerr;        /* number of entries in sys_errlist */
  202.  
  203.     if (errno > sys_nerr || errno < 0) {
  204.     /* there is no entry for it in sys_errlist, build one */
  205.     (void) sprintf(misc_err, "Unknown errno (%d)", errno);
  206.     return misc_err;
  207.     } else {
  208.     /* there is an entry in sys_errlist, use it */
  209.     return sys_errlist[errno];
  210.     }
  211. }
  212.  
  213. /*
  214.  * strsysexit - return a string corresponding to an exit code.
  215.  */
  216. char *
  217. strsysexit(exitcode)
  218.     int exitcode;
  219. {
  220.     static char buf[50];        /* buffer for generating message */
  221.  
  222.     switch (exitcode) {
  223.     case EX_USAGE:
  224.     return "EX_USAGE";
  225.     case EX_DATAERR:
  226.     return "EX_DATAERR";
  227.     case EX_NOINPUT:
  228.     return "EX_NOINPUT";
  229.     case EX_NOUSER:
  230.     return "EX_NOUSER";
  231.     case EX_NOHOST:
  232.     return "EX_NOHOST";
  233.     case EX_UNAVAILABLE:
  234.     return "EX_UNAVAILABLE";
  235.     case EX_SOFTWARE:
  236.     return "EX_SOFTWARE";
  237.     case EX_OSERR:
  238.     return "EX_OSERR";
  239.     case EX_OSFILE:
  240.     return "EX_OSFILE";
  241.     case EX_CANTCREAT:
  242.     return "EX_CANTCREAT";
  243.     case EX_IOERR:
  244.     return "EX_IOERR";
  245.     case EX_TEMPFAIL:
  246.     return "EX_TEMPFAIL";
  247.     case EX_PROTOCOL:
  248.     return "EX_PROTOCOL";
  249.     case EX_NOPERM:
  250.     return "EX_NOPERM";
  251.     default:
  252.     (void) sprintf(buf, "EX_%d", exitcode);
  253.     return buf;
  254.     }
  255. }
  256.  
  257.  
  258. /*
  259.  * str_printf - highly simplified printf to a dynamic string
  260.  * str_printf_va - ditto, but taking a va_list parameter
  261.  *
  262.  * note that we only support %s, %d, %o, %x and %c.  Also support
  263.  * a %z which inserts a null byte (support %N for backward compatibility).
  264.  */
  265. /*VARARGS*/
  266. void
  267. str_printf(sp, fmt, va_alist)
  268.     struct str *sp;            /* append to this string */
  269.     char *fmt;                /* printf-style format string  */
  270.     va_dcl
  271. {
  272.     va_list ap;                /* placeholder for varargs */
  273.  
  274.     va_start(ap);
  275.     str_printf_va(sp, fmt, ap);
  276.     va_end(ap);
  277. }
  278.  
  279. static void
  280. str_printf_va(sp, fmt, ap)
  281.     register struct str *sp;        /* append to this string */
  282.     register char *fmt;            /* printf-style format string */
  283.     va_list ap;                /* placeholder for varargs */
  284. {
  285.     register int c;            /* current char in fmt */
  286.     register int islong;                /* to handle %ld */
  287.     long n;                             /* temp */
  288.     char *s;                /* temp */
  289.  
  290.     /*
  291.      * loop on the format string copying into the string sp
  292.      */
  293.     while (c = *fmt++) {
  294.     if (c != '%') {
  295.         STR_NEXT(sp, c);
  296.     } else {
  297.         if (islong = (*fmt == 'l'))
  298.         fmt++;
  299.         switch (c = *fmt++) {
  300.         case '\0':
  301.         STR_NEXT(sp, '%');
  302.         --fmt;
  303.         break;
  304.         case 's':
  305.         if (s = va_arg(ap, char *)) {
  306.             STR_CAT(sp, s);
  307.         } else {
  308.             STR_CAT(sp, "(null)");
  309.         }
  310.         break;
  311.         case 'c':
  312.         STR_NEXT(sp, va_arg(ap, int));
  313.         break;
  314.         case 'o':
  315.         n = islong ? va_arg(ap,long) : (long)va_arg(ap,unsigned);
  316.         STR_CAT(sp, bltoa(8, n));
  317.         break;
  318.         case 'x':
  319.         n = islong ? va_arg(ap,long) : (long)va_arg(ap,unsigned);
  320.         STR_CAT(sp, bltoa(16, n));
  321.         break;
  322.         case 'u':
  323.         n = islong ? va_arg(ap,long) : (long)va_arg(ap,unsigned);
  324.         STR_CAT(sp, bltoa(10, n));
  325.         break;
  326.         case 'd':
  327.         n = islong ? va_arg(ap,long) : (long)va_arg(ap,int);
  328.         if (n < 0) {
  329.             STR_NEXT(sp, '-');
  330.             n = -n;
  331.         }
  332.         STR_CAT(sp, bltoa(10, n));
  333.         break;
  334.         case 'N':            /* how to insert a nul byte */
  335.         case 'z':
  336.         STR_NEXT(sp, '\0');
  337.         case '%':
  338.         STR_NEXT(sp, '%');
  339.         break;
  340.         default:
  341.         break;
  342.         }
  343.     }
  344.     }
  345.  
  346.     /*
  347.      * add a trailing null, but make sure the next character will
  348.      * overwrite it.
  349.      */
  350.  
  351.     STR_NEXT(sp, '\0');
  352.     sp->i--;
  353. }
  354.  
  355. /*
  356.  * bltoa - convert long integer to string representation in given base
  357.  *
  358.  * standard bug about pointing to static data.
  359.  */
  360. static char *
  361. bltoa(base, n)
  362.     register unsigned base;        /* base for conversion */
  363.     register long     n;                /* number to convert */
  364. {
  365.     static char buf[BITS_PER_INT + 1];    /* plenty big */
  366.     register char *p = buf + sizeof(buf); /* start at end and move backward */
  367.     register int i;
  368.  
  369.     *--p = '\0';            /* terminate string */
  370.     if (n == 0) {
  371.     /* special case, 0 is just "0" */
  372.     *--p = '0';
  373.     return p;
  374.     }
  375.     /* get more significant digits until no more are required */
  376.     while (n > 0) {
  377.     /* allow for bases up to 16 */
  378.     i = n % base;
  379.     n /= base;
  380.     *--p = "0123456789abcdef"[i];
  381.     }
  382.     return p;
  383. }
  384.  
  385. /*
  386.  * xprintf - str_print to a region, returning pointer to region
  387.  */
  388. /*VARARGS1*/
  389. char *
  390. xprintf(fmt, va_alist)
  391.     char *fmt;                /* str_printf-style format string */
  392.     va_dcl
  393. {
  394.     struct str str;
  395.     va_list ap;
  396.  
  397.     STR_INIT(&str);
  398.     va_start(ap);
  399.     str_printf_va(&str, fmt, ap);
  400.     va_end(ap);
  401.     STR_NEXT(&str, '\0');
  402.     STR_DONE(&str);
  403.     return str.p;
  404. }
  405.  
  406. /*
  407.  * dprintf - debugging printf to a file
  408.  *
  409.  * This allows the DEBUGx() macros to explicitly follow the convention that
  410.  * printing a NULL string pointer displays "(null)".  The System V
  411.  * printf() does not follow this convention, and thus cannot be used.
  412.  */
  413. /*VARARGS2*/
  414. int
  415. dprintf(file, fmt, va_alist)
  416.     FILE *file;
  417.     char *fmt;
  418.     va_dcl
  419. {
  420.     static struct str str;
  421.     static int inited = FALSE;
  422.     va_list ap;
  423.  
  424.     if (! inited) {
  425.     inited = 1;
  426.     STR_INIT(&str);
  427.     } else {
  428.     str.i = 0;
  429.     }
  430.     va_start(ap);
  431.     str_printf_va(&str, fmt, ap);
  432.     va_end(ap);
  433.     STR_NEXT(&str, '\0');
  434.  
  435.     return fputs(str.p, file);
  436. }
  437.  
  438. /*
  439.  * c_atol - convert an ascii string to a long, C-style.
  440.  *
  441.  * Handle the forms  [+-]?0[0-7]* [+-]?0x[0-9a-f]* and [+-]?[1-9][0-9]*.
  442.  * an optional suffix of one of the letters:  k, K, or M is allowed, as
  443.  * a multplier by 1024 or 1048576.  If the string given is malformed,
  444.  * error will be set to an error message.
  445.  */
  446. long
  447. c_atol(input, error)
  448.     register char *input;        /* input string */
  449.     char **error;            /* return error message here */
  450. {
  451.     char *input_string = input;
  452.     long val = 0;
  453.     int sign = 1;
  454.     int digval;
  455.     int base = 10;
  456.  
  457.     /* handle a leading sign character */
  458.     if (*input == '-') {
  459.     sign = -1;
  460.     input++;
  461.     } else if (*input == '+') {
  462.     input++;
  463.     }
  464.  
  465.     /* what is the base */
  466.     if (*input == '0') {
  467.     input++;
  468.     base = 8;
  469.     if (*input == 'x' || *input == 'X') {
  470.         base = 16;
  471.         input++;
  472.     }
  473.     }
  474.  
  475.     /* decode the number */
  476.     while (*input) {
  477.     if (isdigit(*input)) {
  478.         digval = *input++ - '0';
  479.     } else {
  480.         if (islower(*input)) {
  481.         digval = *input++ - 'a' + 10;
  482.         } else if (isupper(*input)) {
  483.         digval = *input++ - 'A' + 10;
  484.         } else {
  485.         break;
  486.         }
  487.     }
  488.     if (digval >= base) {
  489.         --input;
  490.         break;
  491.     }
  492.     val = val*base + digval;
  493.     }
  494.  
  495.     /* set the correct sign */
  496.     val *= sign;
  497.  
  498.     /* is there an optional multiplier? */
  499.     if (*input == 'k' || *input == 'K') {
  500.     input++;
  501.     val *= 1024;
  502.     }
  503.     if (*input == 'M') {
  504.     input++;
  505.     val *= 1024 * 1024;
  506.     }
  507.  
  508.     /* there should be nothing left of the input string at this point */
  509.     if (*input) {
  510.     *error = xprintf("invalid number: %s", input_string);
  511.     }
  512.     return val;
  513. }
  514.  
  515. /*
  516.  * base62 - convert a long integer into ASCII base 62
  517.  *
  518.  * uses upper and lowercase letters, plus numbers.
  519.  * always returns a string of exactly 6 characters, plus a null byte.
  520.  *
  521.  * returns a static area which is reused for each call.
  522.  */
  523. char *
  524. base62(val)
  525.     unsigned long val;
  526. {
  527.     static char base62_chars[] =
  528.     "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  529.     static char buf[7];
  530.     register char *p = buf + sizeof(buf) - 1;
  531.  
  532.     *p = '\0';
  533.     while (p > buf) {
  534.     *--p = base62_chars[val % 62];
  535.     val /= 62;
  536.     }
  537.  
  538.     return buf;
  539. }
  540.  
  541. #ifdef notused
  542. /*
  543.  * read_line - read a line from an input stream and return a pointer to it
  544.  *
  545.  * Any trailing newline is stripped from the returned string.
  546.  *
  547.  * returns a static area which may be reused after subsequent calls.
  548.  */
  549. char *
  550. read_line(f)
  551.     register FILE *f;
  552. {
  553.     static int inited = FALSE;
  554.     static struct str line;
  555.     register int c;
  556.  
  557.     if (! inited) {
  558.     inited = TRUE;
  559.     STR_INIT(&line);
  560.     } else {
  561.     line.i = 0;
  562.     }
  563.     while ((c = getc(f)) != '\n' && c != EOF) {
  564.     STR_NEXT(&line, c);
  565.     }
  566.     STR_NEXT(&line, '\0');
  567.     return line.p;
  568. }
  569. #endif    /* notused */
  570.  
  571. /*
  572.  * str_cat - concatenate a C string onto the end of a dynamic string
  573.  * str_ncat - concatenate a C string with a given length onto the
  574.  *          end of a dynamic string
  575.  *
  576.  * Concatenate a string onto the end of a dynamic string region,
  577.  * growing the region as necessary.
  578.  */
  579.  
  580. void
  581. str_cat(sp, s)
  582.     register struct str *sp;
  583.     char *s;
  584. {
  585.     str_ncat(sp, s, (unsigned)strlen(s) + 1);
  586.     sp->i--;
  587. }
  588.  
  589. void
  590. str_ncat(sp, s, n)
  591.     register struct str *sp;
  592.     char *s;
  593.     unsigned n;
  594. {
  595.     if (sp->i + n > sp->a) {
  596.     /*
  597.      * need to expand the region:
  598.      * bump up to a sufficiently large region which is a multiple
  599.      * of STR_BUMP (except for a pointer).  Allow at least 10 free
  600.      * chars in the region, for future expansion.
  601.      */
  602.     sp->a = ((sp->i + n + STR_BUMP + 10) & ~(STR_BUMP-1)) - sizeof(long);
  603.     sp->p = xrealloc(sp->p, sp->a);
  604.     }
  605.     /* copy string to the end of the region, copy the nul byte but
  606.      * don't count it */
  607.     (void) memcpy(sp->p + sp->i, s, n);
  608.     sp->i += n;
  609. }
  610.  
  611. #ifndef HAVE_VFPRINTF
  612. /*
  613.  * vfprintf - a hacked version of vfprintf() for sites that don't have it
  614.  *
  615.  * XXX - will _doprnt() work here?
  616.  */
  617. int
  618. vfprintf(file, fmt, ap)
  619.     FILE *file;
  620.     char *fmt;
  621.     va_list ap;
  622. {
  623.     int a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;
  624.  
  625.     a = va_arg(ap, int);
  626.     b = va_arg(ap, int);
  627.     c = va_arg(ap, int);
  628.     d = va_arg(ap, int);
  629.     e = va_arg(ap, int);
  630.     f = va_arg(ap, int);
  631.     g = va_arg(ap, int);
  632.     h = va_arg(ap, int);
  633.     i = va_arg(ap, int);
  634.     j = va_arg(ap, int);
  635.     k = va_arg(ap, int);
  636.     l = va_arg(ap, int);
  637.     m = va_arg(ap, int);
  638.     n = va_arg(ap, int);
  639.     o = va_arg(ap, int);
  640.  
  641.     return fprintf(file, fmt, a,b,c,d,e,f,g,h,i,j,k,l,m,n,o);
  642. }
  643. #endif    /* HAVE_VFPRINTF */
  644.  
  645. /*
  646.  * ivaltol - convert time interval string into a long integer
  647.  *
  648.  * Take a string defining an interval and convert it into seconds.  An
  649.  * interval is the sum of seconds desribed by terms formed from a
  650.  * decimal integer and a suffix.  The suffix gives a multiplier for
  651.  * the term and can be one of 'y' (years), 'w' (weeks), 'd' (days),
  652.  * 'h' (hours), 'm' (minutes) or 's' (seconds).  If the last term does
  653.  * not have a suffix, 's' is assumed.  For example, an interval string
  654.  * of "1h10m3s" would represent one hour, ten minutes and 30 seconds
  655.  * and would be converted to the equivalent number of seconds.
  656.  *
  657.  * A negative integer is returned for illegal intervals.
  658.  *
  659.  * Note:  interval values are only useful if they fit within an
  660.  *      unsigned integer, so callers of this routine should do range
  661.  *      checking to make sure the value returned is not too large.
  662.  */
  663. long
  664. ivaltol(s)
  665.     register char *s;            /* string containing queue interval */
  666. {
  667.     long ret = 0;            /* return value */
  668.     long cur = 0;            /* value of current part */
  669.     char *ss = s;            /* saved value of s */
  670.  
  671.     while (*s) {
  672.     switch (*s++) {
  673.     case '0': case '1': case '2': case '3': case '4':
  674.     case '5': case '6': case '7': case '8': case '9':
  675.         cur = cur * 10 + s[-1] - '0';
  676.         break;
  677.  
  678.     case 'y':            /* years = 365.24 days */
  679.         ret += (cur * 60*60*24*365) + (cur * 60*60*24*24)/100;
  680.         cur = 0;
  681.         break;
  682.  
  683.     case 'w':            /* weeks */
  684.         ret += cur * 60*60*24*7;
  685.         cur = 0;
  686.         break;
  687.  
  688.     case 'd':            /* days */
  689.         ret += cur * 60*60*24;
  690.         cur = 0;
  691.         break;
  692.  
  693.     case 'h':            /* hours */
  694.         ret += cur * 60*60;
  695.         cur = 0;
  696.         break;
  697.  
  698.     case 'm':
  699.         ret += cur * 60;        /* minutes */
  700.         cur = 0;
  701.         break;
  702.  
  703.     case 's':
  704.         ret += cur;            /* seconds */
  705.         cur = 0;
  706.         break;
  707.  
  708.     default:
  709.         return -1L;
  710.     }
  711.     }
  712.  
  713.     return ret + cur;            /* all done */
  714. }
  715.  
  716. /*
  717.  * ltoival - convert time interval string into a long integer
  718.  *
  719.  * Break down an interval into weeks/days/hours/minutes/seconds,
  720.  * expressing it as it might have been input to ivaltol.
  721.  */
  722. char *
  723. ltoival(n)
  724.     long n;
  725. {
  726.     static char ret[40];
  727.     static char units[] = "smhd";
  728.     int vals[sizeof(units) - 1];
  729.     int i;
  730.  
  731.     if (n < 0) {
  732.     return NULL;
  733.     }
  734.     if (n == 0) {
  735.     return "0";
  736.     }
  737.  
  738.     vals[0] = (n % 60);
  739.     n /= 60;
  740.     vals[1] = (n % 60);
  741.     n /= 60;
  742.     vals[2] = (n % 24);
  743.     n /= 24;
  744.     vals[3] = (n % 7);
  745.     n /= 7;
  746.  
  747.     if (n) {
  748.     sprintf(ret, "%ldw", n);
  749.     } else {
  750.     ret[0] = '\0';
  751.     }
  752.     for (i = sizeof(units) - 2; i >= 0; --i) {
  753.     if (vals[i]) {
  754.         sprintf(ret + strlen(ret), "%d%c", vals[i], units[i]);
  755.     }
  756.     }
  757.     return ret;
  758. }
  759.  
  760. /*
  761.  * copy - copy a NUL-terminated string
  762.  * rcopy - copy a string given start and end+1
  763.  */
  764.  
  765. char *
  766. copy(s)
  767.     char *s;
  768. {
  769.     return rcopy(s, s + strlen(s));
  770. }
  771.  
  772. char *
  773. rcopy(s, e)
  774.     char *s, *e;
  775. {
  776.     char *p;
  777.     int n;
  778.  
  779.     n = (e - s);
  780.     p = xmalloc(n + 1);
  781.     (void) memcpy(p, s, n);
  782.     p[n] = '\0';
  783.     return p;
  784. }
  785.